#include "asm_utils.h"
#include "interrupt.h"
#include "stdio.h"
#include "program.h"
#include "thread.h"
#include "sync.h"


// 屏幕IO处理器
STDIO stdio;
// 中断管理器
InterruptManager interruptManager;
// 程序管理器
ProgramManager programManager;

//#define thinking 0
//#define hungry 1
//#define eating 2
Semaphore chopstick[5];
Semaphore eating;
void Aristotle(void *arg)
{
   while(1)
   {
   	int delay = 0xfffffff;
   	printf("Aristotle: I'm thinking!\n");

   	while(delay)
   	   delay --;
   	printf("Aristotle: I'm hungry!\n");
   	eating.P();
   	
   	chopstick[0].P();
   	
   	delay = 0xfffffff;
   	while(delay)
   	   delay --;
   	chopstick[(0+1)%5].P();
   	printf("Aristotle: I'm having dinner!\n");
   	delay = 0xfffffff;
   	while(delay)
   	   delay --;
   	chopstick[0].V();
   	chopstick[(0+1)%5].V();
   	eating.V();
   }
}
void Socrates(void *arg) 
{
   while(1)
   {
   	int delay = 0xfffffff;
   	printf("Socrates: I'm thinking!\n");

   	while(delay)
   	   delay --;
   	printf("Socrates: I'm hungry!\n");
   	eating.P();
   	chopstick[1].P();
   	
   	delay = 0xfffffff;
   	while(delay)
   	   delay --;
   	chopstick[(1+1)%5].P();
   	printf("Socrates: I'm having dinner!\n");
   	delay = 0xfffffff;
   	while(delay)
   	   delay --;
   	chopstick[1].V();
   	chopstick[(1+1)%5].V();
   	eating.V();
   }
}

void Plato(void *arg)
{
   while(1)
   {
   	int delay = 0xfffffff;
   	printf("Plato: I'm thinking!\n");

   	while(delay)
   	   delay --;
   	printf("Plato: I'm hungry!\n");   
   	eating.P();
   	chopstick[2].P();
   	
   	
   	delay = 0xfffffff;
   	while(delay)
   	   delay --;
   	   
   	chopstick[(2+1)%5].P();
   	
   	printf("Plato: I'm having dinner!\n");
   	delay = 0xfffffff;
   	while(delay)
   	   delay --;
   	chopstick[2].V();
   	chopstick[(2+1)%5].V();
   	eating.V();
   }
}

void Hegel(void *arg)
{
   while(1)
   {
   	int delay = 0xfffffff;
   	printf("Hegel: I'm thinking!\n");
   	
   	while(delay)
   	   delay --;
   	printf("Hegel: I'm hungry!\n");   
   	eating.P();
   	chopstick[3].P();
   	
   	
   	delay = 0xfffffff;
   	while(delay)
   	   delay --;
   	   
   	chopstick[(3+1)%5].P();
   	
   	printf("Hegel: I'm having dinner!\n");
   	delay = 0xfffffff;
   	while(delay)
   	   delay --;
   	chopstick[3].V();
   	chopstick[(3+1)%5].V();
   	eating.V();
   }
}
void Kant(void *arg)
{
   while(1)
   {
   	int delay = 0xfffffff;
   	printf("Kant: I'm thinking!\n");
   	
   	while(delay)
   	   delay --;
   	printf("Kant: I'm hungry!\n");
   	eating.P();
   	chopstick[4].P();
   	
   	delay = 0xfffffff;
   	while(delay)
   	   delay --;
   	chopstick[(4+1)%5].P();
   	printf("Kant: I'm having dinner!\n");
   	delay = 0xfffffff;
   	while(delay)
   	   delay --;
   	chopstick[4].V();
   	chopstick[(4+1)%5].V();
   	eating.V();
   }
}
void first_thread(void *arg)
{
    // 第1个线程不可以返回
    stdio.moveCursor(0);
    for (int i = 0; i < 25 * 80; ++i)
    {
        stdio.print(' ');
    }
    stdio.moveCursor(0);

    for (int i = 0; i<5; i++)
    {
    	chopstick[i].initialize(1);
    	
    }
    eating.initialize(4);
	
    programManager.executeThread(Aristotle, nullptr, "second thread", 1);
    programManager.executeThread(Socrates, nullptr, "third thread", 1);
    programManager.executeThread(Plato, nullptr, "forth thread", 1);
    programManager.executeThread(Hegel, nullptr, "fifth thread", 1);
    programManager.executeThread(Kant, nullptr, "sixth thread", 1);
    asm_halt();
}

extern "C" void setup_kernel()
{

    // 中断管理器
    interruptManager.initialize();
    interruptManager.enableTimeInterrupt();
    interruptManager.setTimeInterrupt((void *)asm_time_interrupt_handler);

    // 输出管理器
    stdio.initialize();

    // 进程/线程管理器
    programManager.initialize();

    // 创建第一个线程
    int pid = programManager.executeThread(first_thread, nullptr, "first thread", 1);
    if (pid == -1)
    {
        printf("can not execute thread\n");
        asm_halt();
    }

    ListItem *item = programManager.readyPrograms.front();
    PCB *firstThread = ListItem2PCB(item, tagInGeneralList);
    firstThread->status = RUNNING;
    programManager.readyPrograms.pop_front();
    programManager.running = firstThread;
    asm_switch_thread(0, firstThread);

    asm_halt();
}
